<?php

class RssSpider
{
    const UNDEFINED_TOKEN = "undefined";
    const RSS_2_0_TOKEN   = "rss2.0";
    const RSS_1_0_TOKEN   = "rss1.0";
    const ATOM_TOKEN      = "atom";

    /**
     * @var RssInfo
     */
    private $sourceInfo;
    /**
     * @var IRSSParser
     */
    private $rssParser;

    private $commonAuthor;

    private $maxPostsCount;

    private $maxPostsTotal;

    private $fetchedPostsCount;

    public function __construct($maxPostsCount, $maxPostsTotal)
    {
        $this->maxPostsCount = $maxPostsCount;
        $this->maxPostsTotal = $maxPostsTotal;
        $this->fetchedPostsCount = 0;
    }

    public function RetrieveFeed(RssInfo $sourceInfo)
    {
        $this->sourceInfo = $sourceInfo;
        $rssInfo = null;
        $sourceUrl = $sourceInfo->GetSourceUrl();
        $rssResource = RssSpiderUtils::GetResource($sourceUrl);
        if ( !empty($rssResource) )
        {
            $this->rssParser = $this->CreateParser($rssResource);
            if ( $this->rssParser )
            {
                $rssInfo = $this->FetchRssInfo();
            }
            else
            {
                Logger::Log(SPIDER_LOG_FILE, "Undefined rss format: " . $sourceUrl);
            }
        }
        else
        {
            Logger::Log(SPIDER_LOG_FILE, "Empty rss resource on url: " . $sourceUrl);
        }
        return $rssInfo;
    }

    public function GetFetchedPostsCount()
    {
        return $this->fetchedPostsCount;
    }

    private function FetchRssInfo()
    {
        $rssParser = $this->rssParser;
        $rssInfo = null;
        $mainNode = $rssParser->GetMainNode();

        $lastModifiedDate = $rssParser->GetDate($mainNode);
        $lastModifiedDate = DateUtils::ConvertDateString($lastModifiedDate, DateUtils::ISO_DATETIME_PATTERN);

        if ($this->sourceInfo->GetLastModifiedDate() != $lastModifiedDate)
        {
            $rssInfo = new RssInfo();
            $rssInfo->SetLastModifiedDate($lastModifiedDate);

            $title = $rssParser->GetTitle($mainNode);
            $rssInfo->SetTitle($title);

            $description = $rssParser->GetDescription($mainNode);
            $rssInfo->SetDescription($description);

            $link = $rssParser->GetLink($mainNode);
            $rssInfo->SetLink($link);

            $this->commonAuthor = $rssParser->GetAuthor($mainNode);

            $rssPosts = $this->GetRssPosts();
            $rssInfo->SetPosts($rssPosts);

            $rssInfo->SetRssInfoId($this->sourceInfo->GetRssInfoId());
        }

        return $rssInfo;
    }

    public function GetRssPosts()
    {
        $itemNodes = $this->rssParser->GetItemNodes();
        $postsArray = array();
        foreach ($itemNodes as $node)
        {
            if ((count($postsArray) >= $this->maxPostsCount) ||
                ($this->fetchedPostsCount >= $this->maxPostsTotal))
            {
                break;
            }
            $post = $this->GetPost($node);
            if ($post)
            {
                array_push($postsArray, $post);
                $this->fetchedPostsCount++;
            }
        }
        return $postsArray;
    }

    /** Gets RssPost from a given node
     *  @param $node DOMElement containing post info
     *  @return RssPost
     */
    protected function GetPost($node)
    {
        $rssParser = $this->rssParser;

        $link = $rssParser->GetLink($node);
        $creationDate = $rssParser->GetDate($node);
        $creationDate = DateUtils::ConvertDateString($creationDate, DateUtils::ISO_DATETIME_PATTERN);
        $rssPost = RssPostManager::GetPostByUrl($link);

        if (($rssPost == null) || ($rssPost && ($creationDate != $rssPost->GetCreationDate())))
        {
            $rssPost = (!$rssPost) ? new RssPost() : $rssPost;
            $rssPost->SetPostLink($link);
            $rssPost->SetCreationDate($creationDate);

            $title = $rssParser->GetTitle($node);
            $rssPost->SetTitle($title);

            $description = $rssParser->GetDescription($node);
            $rssPost->SetDescription($description);

            $content = $rssParser->GetPostContent($node);
            $rssPost->SetContent($content);

            if ( empty($description) && !empty($content) )
            {
                $rssPost->SetDescription(RssSpiderUtils::GetShortContent($content));
            }

            $postImage = $rssParser->GetPostImage($node, $this->sourceInfo->GetSourceUrl());
            $rssPost->SetImage($postImage);

            $author = $rssParser->GetAuthor($node);
            $author = ( !empty($author) ) ? $author : $this->commonAuthor;
            $rssPost->SetAuthor($author);

            $categories = $rssParser->GetCategories($node);
            $rssPost->SetCategories($categories);
        }
        return $rssPost;
    }

    /** Creates parser according to content of resource
     *  @param string $rssResource the resource to be parsed.
     *  @return IRssParser object on success, null on failure.
     */
    private function CreateParser( $rssResource )
    {
        $parser = null;
        $rssDomDoc = new DOMDocument();
        if ( $rssDomDoc->loadXML($rssResource, LIBXML_NOERROR) )
        {
            $parserType = $this->DefineParser($rssDomDoc);
            switch ($parserType)
            {
            case self::RSS_1_0_TOKEN :
                $parser = new Rss1Parser($rssDomDoc);
                break;
            case self::RSS_2_0_TOKEN :
                $parser = new Rss2Parser($rssDomDoc);
                break;
            case self::ATOM_TOKEN :
                $parser = new AtomParser($rssDomDoc);
                break;
            }
        }
        return $parser;
    }

    private function DefineParser( $domDocument )
    {
        $xPathHelper = new XPathHelper($domDocument);
        $result = self::UNDEFINED_TOKEN;

        if ( $xPathHelper->GetNode('.')->tagName == 'rss' )
        {
            $result = self::RSS_2_0_TOKEN;
        }
        else if ( $xPathHelper->GetNode('.')->tagName == 'feed' )
        {
            $result = self::ATOM_TOKEN;
        }
        else if ( $xPathHelper->GetNode('.')->tagName == "rdf:RDF" )
        {
            $result = self::RSS_1_0_TOKEN;
        }

        return $result;
    }

}

?>